﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

using mkv16;

namespace mkv16
{
    [Serializable]
    public class MemoryStore : IMemoryStore
    {
        string filename;

        List<Notion> notions;
        List<Word> words;
        List<Triple> triples;
        List<Fact> facts;
        List<Source> sources;

        TimeSpan delayedWriteFrequency;
        DateTime lastSave;
        TimeSpan delaySinceLastSave;

        public MemoryStore()
        {
            init("default.memory", new TimeSpan(0, 0, 5, 0, 0));
        }
        public MemoryStore(string filename)
        {
            init(filename, new TimeSpan(0, 0, 5, 0, 0));
        }
        public MemoryStore(string filename, TimeSpan delayedWriteFrequency)
        {
            init(filename, delayedWriteFrequency);
        }

        private void init(string filename, TimeSpan delayedWriteFrequency)
        {
            this.filename = filename;
            this.delayedWriteFrequency = delayedWriteFrequency;

            if (File.Exists(filename))
            {
                Load(filename);
                lastSave = DateTime.Now;
            }
            else
            {
                Purge();
            }
        }

        public void Save()
        {
            Save(filename);
        }

        public void Save(string filename)
        {
            var data = this;
            var directory = Path.GetDirectoryName(filename);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
            Stream stream = File.Open(filename, FileMode.Create);
            BinaryFormatter bFormatter = new BinaryFormatter();
            bFormatter.Serialize(stream, data);
            stream.Close();

            lastSave = DateTime.Now;
        }

        public void Load(string filename)
        {
            MemoryStore data;
            Stream stream = File.Open(filename, FileMode.Open);
            BinaryFormatter bFormatter = new BinaryFormatter();
            data = (MemoryStore)bFormatter.Deserialize(stream);
            stream.Close();

            this.notions = data.notions;
            this.words = data.words;
            this.triples = data.triples;
            this.facts = data.facts;
            this.sources = data.sources;
        }

        public void Purge()
        {
            this.notions = new List<Notion>();
            this.words = new List<Word>();
            this.triples = new List<Triple>();
            this.facts = new List<Fact>();
            this.sources = new List<Source>();
        }

        public List<Notion> Notions
        {
            get { return notions; }
        }
        public List<Word> Words
        {
            get { return words; }
        }
        public List<Triple> Triples
        {
            get { return triples; }
        }
        public List<Fact> Facts
        {
            get { return facts; }
        }
        public List<Source> Sources
        {
            get { return sources; }
        }

        public void AddNotion(Notion notion)
        {
            notions.Add(notion);

            delaySinceLastSave = DateTime.Now - lastSave;
            if (delaySinceLastSave.TotalSeconds > delayedWriteFrequency.TotalSeconds)
            {
                Save();
            }
        }
        public void AddWord(Word notion)
        {
            AddNotion(notion);
            words.Add(notion);
        }
        public void AddTriples(Triple notion)
        {
            AddNotion(notion);
            triples.Add(notion);
        }
        public void AddFact(Fact notion)
        {
            AddNotion(notion);
            facts.Add(notion);
        }
        public void AddSource(Source source)
        {
            sources.Add(source);
        }
        public IEnumerable<Notion> Match(string word)
        {
            // sort descending length so that longest items can be matched first
            var matches = from f in notions
                          where f.Name.StartsWith(word)
                          orderby f.Name.Length descending
                          select f;

            return matches;
        }
        public static IEnumerable<Notion> Match(string word, IEnumerable<Notion> source)
        {
            // sort descending length so that longest items can be matched first
            var matches = from f in source
                          where f.Name.StartsWith(word)
                          orderby f.Name.Length descending
                          select f;

            return matches;
        }
    }
}
